﻿namespace ImapComponent.Net.Mail.Imap4
{
    using System;
    using System.IO;
    using System.Net.Mail;

    using ImapComponent.Net.Mail.Mime;
    using ImapComponent.Net.Mail.Utils;

    /// <summary>
    /// Represents the mail envelope.
    /// </summary>
    public class Envelope
    {
        #region Fields

        private readonly MailAddress from;
        private readonly MailAddress replyTo;
        private readonly MailAddress sender;
        private readonly MailAddressCollection bcc = new MailAddressCollection();
        private readonly MailAddressCollection cc = new MailAddressCollection();
        private readonly MailAddressCollection to = new MailAddressCollection();
        private readonly DateTime date;
        private readonly DateTime dateReceived;
        private readonly MessageFlagSet flags;
        private readonly string inReplyTo;
        private readonly string messageId;
        private readonly string subject;
        private readonly int size;
        private readonly int messageNumber;
        private readonly int uid;
        private readonly bool isValid;

        #endregion // Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the Envelope class.
        /// </summary>
        /// <param name="envelopeString">The envelope string.</param>
        private Envelope(string envelopeString)
        {
            try
            {
                string[] messageNumberParse = envelopeString.Split(new char[] { ' ' }, 4);

                this.messageNumber = Int32.Parse(messageNumberParse[1]);

                string stringToParse = messageNumberParse[3].Trim(new char[] { '(', ')' });

                while (!string.IsNullOrEmpty(stringToParse))
                {
                    /// parses uid 
                    if (stringToParse.StartsWith("UID"))
                    {
                        string[] parsedStrings = stringToParse.Split(new char[] { ' ' }, 3);
                        this.uid = Int32.Parse(parsedStrings[1]);
                        stringToParse = parsedStrings.Length == 3 ? parsedStrings[2] : string.Empty;
                    }
                    /// parses size
                    else if (stringToParse.StartsWith("RFC822.SIZE"))
                    {
                        string[] parsedStrings = stringToParse.Split(new char[] { ' ' }, 3);
                        this.size = Int32.Parse(parsedStrings[1]);
                        stringToParse = parsedStrings.Length == 3 ? parsedStrings[2] : string.Empty;
                    }
                    /// parses date received
                    else if (stringToParse.StartsWith("INTERNALDATE"))
                    {
                        string[] parsedStrings = stringToParse.Split(new char[] { '"' }, 3);
                        this.dateReceived = Parser.ParseRfc2822Date(parsedStrings[1]);
                        stringToParse = parsedStrings.Length == 3 ? parsedStrings[2].Trim() : string.Empty;
                    }
                    /// parses flags
                    else if (stringToParse.StartsWith("FLAGS"))
                    {
                        string[] parsedStrings = stringToParse.Split(new char[] { ' ' }, 2);
                        string flagsString = parsedStrings[1].GetStringWithinBrackets();
                        string[] flagsArray = flagsString.Split(' ');
                        this.flags = MessageFlagSet.CreateMessageFlagSet(flagsArray, Helper.GetSystemFlags(flagsArray));
                        stringToParse = parsedStrings[1].Substring(flagsString.Length + 2).Trim();
                    }
                    /// parses envelope
                    else if (stringToParse.StartsWith("ENVELOPE"))
                    {
                        string[] parsedStrings = stringToParse.Split(new char[] { ' ' }, 2);
                        string envelopeBodyString = parsedStrings[1].Trim(new char[] { '(', ')' });
                        string[] parsedArray = envelopeBodyString.Split(new char[] { '"' }, 3);
                        string parsedDateFromString = parsedArray[1];

                        /// parses date
                        this.date = Parser.ParseRfc2822Date(parsedDateFromString);
                        string subStringToParse = parsedArray[2].Trim();

                        /// parses subject
                        if (subStringToParse.StartsWith("NIL"))
                        {
                            this.subject = string.Empty;
                            subStringToParse = subStringToParse.Substring("NIL ".Length);
                        }
                        else
                        {
                            string subjectString = subStringToParse.GetQuotedString();
                            this.subject = StringEncoder.DecodeHeaderValue(subjectString);
                            subStringToParse = subStringToParse.Substring(subjectString.Length + 2).Trim();
                        }

                        /// parses from
                        if (subStringToParse.StartsWith("("))
                        {
                            subStringToParse = subStringToParse.Substring(1);
                            string fromString = Helper.GetMailAddressString(subStringToParse);
                            subStringToParse = subStringToParse.Substring(fromString.Length + 2).Trim();

                            if (fromString != "NIL NIL NIL NIL")
                            {
                                try
                                {
                                    this.from = Parser.ParseMailAddress(fromString);
                                }
                                catch
                                {
                                    /// invalid mailbox name, skip mailbox
                                }
                            }

                            while (subStringToParse.StartsWith("("))
                            {
                                string temp = Helper.GetMailAddressString(subStringToParse);
                                subStringToParse = subStringToParse.Substring(temp.Length + 2).Trim();
                            }

                            if (subStringToParse.StartsWith(")"))
                            {
                                subStringToParse = subStringToParse.Substring(1).Trim();
                            }
                            else
                            {
                                throw new InvalidDataException("Invalid envelope data.");
                            }
                        }

                        /// parses sender
                        if (subStringToParse.StartsWith("("))
                        {
                            subStringToParse = subStringToParse.Substring(1);
                            string senderString = Helper.GetMailAddressString(subStringToParse);
                            subStringToParse = subStringToParse.Substring(senderString.Length + 2).Trim();

                            if (senderString != "NIL NIL NIL NIL")
                            {
                                try
                                {
                                    this.sender = Parser.ParseMailAddress(senderString);
                                }
                                catch
                                {
                                    /// invalid mailbox name, skip mailbox
                                }
                            }

                            while (subStringToParse.StartsWith("("))
                            {
                                string temp = Helper.GetMailAddressString(subStringToParse);
                                subStringToParse = subStringToParse.Substring(temp.Length + 2).Trim();
                            }

                            if (subStringToParse.StartsWith(")"))
                            {
                                subStringToParse = subStringToParse.Substring(1).Trim();
                            }
                            else
                            {
                                throw new InvalidDataException("Invalid envelope data.");
                            }
                        }

                        /// parses reply to
                        if (subStringToParse.StartsWith("("))
                        {
                            subStringToParse = subStringToParse.Substring(1);
                            string replyToString = Helper.GetMailAddressString(subStringToParse);
                            subStringToParse = subStringToParse.Substring(replyToString.Length + 2).Trim();

                            if (replyToString != "NIL NIL NIL NIL")
                            {
                                try
                                {
                                    this.replyTo = Parser.ParseMailAddress(replyToString);
                                }
                                catch
                                {
                                    /// invalid mailbox name, skip mailbox
                                }
                            }

                            while (subStringToParse.StartsWith("("))
                            {
                                string temp = Helper.GetMailAddressString(subStringToParse);
                                subStringToParse = subStringToParse.Substring(temp.Length + 2).Trim();
                            }

                            if (subStringToParse.StartsWith(")"))
                            {
                                subStringToParse = subStringToParse.Substring(1).Trim();
                            }
                            else
                            {
                                throw new InvalidDataException("Invalid envelope data.");
                            }
                        }

                        /// parses to
                        if (subStringToParse.StartsWith("NIL"))
                        {
                            subStringToParse = subStringToParse.Substring("NIL ".Length);
                        }
                        else
                        {
                            if (subStringToParse.StartsWith("("))
                            {
                                subStringToParse = subStringToParse.Substring(1);

                                while (subStringToParse.StartsWith("("))
                                {
                                    string toString = Helper.GetMailAddressString(subStringToParse);
                                    subStringToParse = subStringToParse.Substring(toString.Length + 2).Trim();

                                    if (toString != "NIL NIL NIL NIL")
                                    {
                                        try
                                        {
                                            this.to.Add(Parser.ParseMailAddress(toString));
                                        }
                                        catch
                                        {
                                            /// invalid mailbox name, skip mailbox
                                        }
                                    }
                                }

                                if (subStringToParse.StartsWith(")"))
                                {
                                    subStringToParse = subStringToParse.Substring(1).Trim();
                                }
                                else
                                {
                                    throw new InvalidDataException("Invalid envelope data.");
                                }
                            }
                        }

                        /// parses cc
                        if (subStringToParse.StartsWith("NIL"))
                        {
                            subStringToParse = subStringToParse.Substring("NIL ".Length);
                        }
                        else
                        {
                            if (subStringToParse.StartsWith("("))
                            {
                                subStringToParse = subStringToParse.Substring(1);

                                while (subStringToParse.StartsWith("("))
                                {
                                    string ccString = Helper.GetMailAddressString(subStringToParse);
                                    subStringToParse = subStringToParse.Substring(ccString.Length + 2).Trim();

                                    if (ccString != "NIL NIL NIL NIL")
                                    {
                                        try
                                        {
                                            this.cc.Add(Parser.ParseMailAddress(ccString));
                                        }
                                        catch
                                        {
                                            /// invalid mailbox name, skip mailbox
                                        }
                                    }
                                }

                                if (subStringToParse.StartsWith(")"))
                                {
                                    subStringToParse = subStringToParse.Substring(1).Trim();
                                }
                                else
                                {
                                    throw new InvalidDataException("Invalid envelope data.");
                                }
                            }
                        }

                        /// parses bcc
                        if (subStringToParse.StartsWith("NIL"))
                        {
                            subStringToParse = subStringToParse.Substring("NIL ".Length);
                        }
                        else
                        {
                            if (subStringToParse.StartsWith("("))
                            {
                                subStringToParse = subStringToParse.Substring(1);

                                while (subStringToParse.StartsWith("("))
                                {
                                    string bccString = Helper.GetMailAddressString(subStringToParse);
                                    subStringToParse = subStringToParse.Substring(bccString.Length + 2).Trim();

                                    if (bccString != "NIL NIL NIL NIL")
                                    {
                                        try
                                        {
                                            this.bcc.Add(Parser.ParseMailAddress(bccString));
                                        }
                                        catch
                                        {
                                            /// invalid mailbox name, skip mailbox
                                        }
                                    }
                                }

                                if (subStringToParse.StartsWith(")"))
                                {
                                    subStringToParse = subStringToParse.Substring(1).Trim();
                                }
                                else
                                {
                                    throw new InvalidDataException("Invalid envelope data.");
                                }
                            }
                        }

                        /// parses in reply to
                        if (subStringToParse.StartsWith("NIL"))
                        {
                            this.inReplyTo = string.Empty;
                            subStringToParse = subStringToParse.Substring("NIL ".Length);
                        }
                        else
                        {
                            this.inReplyTo = subStringToParse.GetQuotedString();
                            subStringToParse = subStringToParse.Substring(this.inReplyTo.Length + 3);
                        }

                        /// parses message id
                        this.messageId = subStringToParse.StartsWith("NIL") ? string.Empty : subStringToParse.GetQuotedString();

                        stringToParse = string.Empty;
                    }
                }

                this.isValid = true;
            }
            catch
            {
                this.isValid = false;
            }
        }

        /// <summary>
        /// Factory method.
        /// </summary>
        public static Envelope CreateEnvelope(string envelopeString)
        {
            return new Envelope(envelopeString);
        }

        #endregion // Constructors

        #region Properties

        /// <summary>
        /// Gets the from email address.
        /// </summary>
        public MailAddress From
        {
            get
            {
                return this.from;
            }
        }

        /// <summary>
        /// Gets the replyTo email address.
        /// </summary>
        public MailAddress ReplyTo
        {
            get
            {
                return this.replyTo;
            }
        }

        /// <summary>
        /// Gets the sender email address.
        /// </summary>
        public MailAddress Sender
        {
            get
            {
                return this.sender;
            }
        }

        /// <summary>
        /// Gets the address collection that contains the blind carbon copy (BCC) recipients for this e-mail message. 
        /// </summary>
        public MailAddressCollection Bcc
        {
            get
            {
                return this.bcc;
            }
        }

        /// <summary>
        /// Gets the address collection that contains the carbon copy (CC) recipients for this e-mail message.
        /// </summary>
        public MailAddressCollection Cc
        {
            get
            {
                return this.cc;
            }
        }

        /// <summary>
        /// Gets the address collection that contains the recipients of this e-mail message.
        /// </summary>
        public MailAddressCollection To
        {
            get
            {
                return this.to;
            }
        }

        /// <summary>
        /// Gets the date when the message was composed.
        /// </summary>
        public DateTime Date
        {
            get
            {
                return this.date;
            }
        }

        /// <summary>
        /// Gets the date when the message was received.
        /// </summary>
        public DateTime DateReceived
        {
            get
            {
                return this.dateReceived;
            }
        }

        /// <summary>
        /// Gets the message flags.
        /// </summary>
        public MessageFlagSet Flags
        {
            get
            {
                return this.flags;
            }
        }

        /// <summary>
        /// Gets the InReplyTo value.
        /// </summary>
        public string InReplyTo
        {
            get
            {
                return this.inReplyTo;
            }
        }

        /// <summary>
        /// Gets the message id.
        /// </summary>
        public string MessageId
        {
            get
            {
                return this.messageId;
            }
        }

        /// <summary>
        /// Gets or sets the subject line for this e-mail message.
        /// </summary>
        public string Subject
        {
            get
            {
                return this.subject;
            }
        }

        /// <summary>
        /// Gets the message size.
        /// </summary>
        public int Size
        {
            get
            {
                return this.size;
            }
        }

        /// <summary>
        /// Gets the message number.
        /// </summary>
        public int MessageNumber
        {
            get
            {
                return this.messageNumber;
            }
        }

        /// <summary>
        /// Gets the message uid.
        /// </summary>
        public int Uid
        {
            get
            {
                return this.uid;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the envelope was successfuly parsed or not.
        /// </summary>
        public bool IsValid
        {
            get
            {
                return this.isValid;
            }
        }

        #endregion // Properties
    }
}
